package com.demo.dbcompare.model.dto;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import com.demo.dbcompare.model.CompareUtils;
import com.demo.dbcompare.model.TableColumnInfo;
import com.demo.dbcompare.model.vo.DbCompareDiffVo;
import com.google.common.base.Function;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Collections2;
import com.google.common.collect.Multimap;

/**
 * 表对应关系集合+表差异集合
 * @author: jie.deng
 * @time: 2018年1月10日 下午6:01:52
 */
public class TableDiff {

	// 左连接名
	private String connNameLeft;
	// 右连接名
	private String connNameRight;
	/**
	 * 左右连接表对应关系信息
	 */
	private List<TableRelationDto> tableRelationList = new ArrayList<>();

	/**
	 * 左右连接表差异信息
	 */
	private Set<String> tableNameLeftMissedSet = new TreeSet<>();
	private Set<String> tableNameRightMissedSet = new TreeSet<>();

	public List<DbCompareDiffVo> genSimilarities(
			String connNameLeft, 
			String connNameRight,
			List<String> tableNameListLeft, 
			List<String> tableNameListRight, 
			List<TableColumnInfo> tableColumnInfoListLeft,
			List<TableColumnInfo> tableColumnInfoListRight){
		this.connNameLeft = connNameLeft;
		this.connNameRight = connNameRight;
		//表差异
		genTableDiff(tableNameListLeft, tableNameListRight, tableColumnInfoListLeft, tableColumnInfoListRight);
		//建立表和列对应关系
		combineTableAndColumn(tableColumnInfoListLeft, tableColumnInfoListRight);
		//列差异
		return genColumnSimilarities();		
	}
			
	private List<DbCompareDiffVo> genColumnSimilarities() {
		List<DbCompareDiffVo> dbCompareSimilarities = new ArrayList<>();
		// 左右连接都有表信息，但是字段结构存在差异的，每个字段产生一条差异记录
		for (TableRelationDto tableRelation : this.tableRelationList) {
			dbCompareSimilarities.addAll(tableRelation.genColumnSimilarities());
		}
		
		return dbCompareSimilarities;
	}

	public List<DbCompareDiffVo> genDiff(
			String connNameLeft, 
			String connNameRight,
			List<String> tableNameListLeft, 
			List<String> tableNameListRight, 
			List<TableColumnInfo> tableColumnInfoListLeft,
			List<TableColumnInfo> tableColumnInfoListRight) {
		this.connNameLeft = connNameLeft;
		this.connNameRight = connNameRight;
		//表差异
		genTableDiff(tableNameListLeft, tableNameListRight, tableColumnInfoListLeft, tableColumnInfoListRight);
		//建立表和列对应关系
		combineTableAndColumn(tableColumnInfoListLeft, tableColumnInfoListRight);
		//列差异
		return genColumnDiff();
	}

	private void combineTableAndColumn(List<TableColumnInfo> tableColumnInfoListLeft,
			List<TableColumnInfo> tableColumnInfoListRight) {
		Multimap<String,TableColumnInfo> tableColumnInfoMapLeft = ArrayListMultimap.create(); 
		if (CollectionUtils.isNotEmpty(tableColumnInfoListLeft)) {
			for (TableColumnInfo tableColumnInfo : tableColumnInfoListLeft) {
				tableColumnInfoMapLeft.put(tableColumnInfo.getTableName(), tableColumnInfo);
			}
		}
		Multimap<String,TableColumnInfo> tableColumnInfoMapRight = ArrayListMultimap.create(); 
		if (CollectionUtils.isNotEmpty(tableColumnInfoListRight)) {
			for (TableColumnInfo tableColumnInfo : tableColumnInfoListRight) {
				tableColumnInfoMapRight.put(tableColumnInfo.getTableName(), tableColumnInfo);
			}
		}
		for (TableRelationDto tableRelationDto : this.tableRelationList) {
			String tableNameLeft = tableRelationDto.getTableNameLeft();
			String tableNameRight = tableRelationDto.getTableNameRight();
			Collection<TableColumnInfo> listLeft = tableColumnInfoMapLeft.get(tableNameLeft);
			Collection<TableColumnInfo> listRight = tableColumnInfoMapRight.get(tableNameRight);
			if (CollectionUtils.isEmpty(listLeft) && CollectionUtils.isEmpty(listRight)) {
				continue ;	//在数据库中未查询到页面输入的左右连接的表名(tableNameLeft,tableNameRight)
			}else if (CollectionUtils.isNotEmpty(listLeft) && CollectionUtils.isEmpty(listRight)) {
				tableNameRightMissedSet.add(tableNameLeft);//在数据库中未查询到页面输入的右连接的表名(tableNameLeft,tableNameRight)
			}else if (CollectionUtils.isEmpty(listLeft) && CollectionUtils.isNotEmpty(listRight)) {
				tableNameLeftMissedSet.add(tableNameRight);//在数据库中未查询到页面输入的左连接的表名(tableNameLeft,tableNameRight)
			}else {
				tableRelationDto.setTableColumnInfoLeft(new ArrayList<>(listLeft));
				tableRelationDto.setTableColumnInfoRight(new ArrayList<>(listRight));
			}			
		}
	}

	private void genTableDiff(List<String> tableNameListLeft, 
			List<String> tableNameListRight,
			List<TableColumnInfo> tableColumnInfoListLeft, 
			List<TableColumnInfo> tableColumnInfoListRight) {
		if (CollectionUtils.isNotEmpty(tableNameListLeft)
				|| CollectionUtils.isNotEmpty(tableNameListRight)) {
			int sizeLeft = tableNameListLeft.size();
			int sizeRight = tableNameListRight.size();
			int size = Math.min(sizeLeft, sizeRight);
			if (sizeLeft > size) {
				tableNameRightMissedSet.addAll(tableNameListLeft.subList(size, sizeLeft));
			} else if (sizeRight > size) {
				tableNameLeftMissedSet.addAll(tableNameListRight.subList(size, sizeRight));
			}
			for (int i = 0; i < size; i++) {
				String tableNameLeft = tableNameListLeft.get(i);
				String tableNameRight = tableNameListRight.get(i);
				if (StringUtils.isBlank(tableNameLeft) && StringUtils.isBlank(tableNameRight)) {
					continue ;
				}else if (StringUtils.isNotBlank(tableNameLeft) && StringUtils.isBlank(tableNameRight)) {
					tableNameRightMissedSet.add(tableNameLeft);
				}else if (StringUtils.isBlank(tableNameLeft) && StringUtils.isNotBlank(tableNameRight)) {
					tableNameLeftMissedSet.add(tableNameRight);
				}else {
					tableRelationList.add(new TableRelationDto(this.connNameLeft, tableNameLeft, this.connNameRight, tableNameRight));
				}
			}			
		}else {
			Collection<String> transformLeft = Collections2.transform(tableColumnInfoListLeft, new Function<TableColumnInfo, String>() {
				@Override
				public String apply(TableColumnInfo tableColumnInfo) {
					return tableColumnInfo.getTableName();
				}
			});
			List<String> tableNamesLeft = new ArrayList<>();
			tableNamesLeft.addAll(new HashSet<>(transformLeft));
			
			Collection<String> transformRight = Collections2.transform(tableColumnInfoListRight, new Function<TableColumnInfo, String>() {
				@Override
				public String apply(TableColumnInfo tableColumnInfo) {
					return tableColumnInfo.getTableName();
				}
			});
			List<String> tableNamesRight = new ArrayList<>();
			tableNamesRight.addAll(new HashSet<>(transformRight));
			//表名相同的建立表对应关系
			List<String> intersect = CompareUtils.intersect(tableNamesLeft, tableNamesRight);
			for (String tableName : intersect) {
				tableRelationList.add(new TableRelationDto(this.connNameLeft, tableName, this.connNameRight, tableName));
			}
			tableNameLeftMissedSet.addAll(CompareUtils.subtract(tableNamesRight, intersect));
			tableNameRightMissedSet.addAll(CompareUtils.subtract(tableNamesLeft, intersect));
		}
	}

	private List<DbCompareDiffVo> genColumnDiff() {
		List<DbCompareDiffVo> dbCompareDiffList = new ArrayList<>();
		// 左连接有表信息，右连接表缺失的数据，每张表产生一条差异记录
		for (String tableName : this.tableNameRightMissedSet) {
			dbCompareDiffList.addAll(CompareUtils.genDefaultTableDiff(connNameLeft, connNameRight, tableName, ""));
		}
		// 右连接有表信息，左连接表缺失的数据，每张表产生一条差异记录
		for (String tableName : this.tableNameLeftMissedSet) {
			dbCompareDiffList.addAll(CompareUtils.genDefaultTableDiff(connNameLeft, connNameRight, "", tableName));
		}
		// 左右连接都有表信息，但是字段结构存在差异的，每个字段产生一条差异记录
		for (TableRelationDto tableRelation : this.tableRelationList) {
			dbCompareDiffList.addAll(tableRelation.genColumnDiff());
		}
		
		return dbCompareDiffList;
	}

	public String getConnNameLeft() {
		return connNameLeft;
	}

	public String getConnNameRight() {
		return connNameRight;
	}

	public List<TableRelationDto> getTableRelationList() {
		return tableRelationList;
	}

	public Set<String> getTableNameLeftMissedSet() {
		return tableNameLeftMissedSet;
	}

	public Set<String> getTableNameRightMissedSet() {
		return tableNameRightMissedSet;
	}
	
}
